package com.apes.framework.jpa.lock;

/**
 * 功能：拦截器（悲观锁和乐观锁重试机制）
 *
 * @author xul
 * @create 2018-02-12 14:47
 */

import com.apes.framework.plugin.esb.api.Request;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.core.Ordered;
import org.springframework.dao.ConcurrencyFailureException;
import org.springframework.dao.OptimisticLockingFailureException;
import org.springframework.dao.PessimisticLockingFailureException;
import org.springframework.stereotype.Component;

@Aspect
@Component
public class ConcurrentOperationExecutor implements Ordered {
    protected final Logger logger = LogManager.getLogger(ConcurrentOperationExecutor.class);

    /**
     * 系统乐观锁尝试次数
     **/
    @Value("${lock.retries:5}")
    private int retries;

    private static final int DEFAULT_MAX_RETRIES = 5;
    private int maxRetries = DEFAULT_MAX_RETRIES;


    @Override
    public int getOrder() {
        return 1;
    }

    @Around(value = "@annotation(isTryAgain)")
    public Object operation(ProceedingJoinPoint point, IsTryAgain isTryAgain) throws Throwable {
        int retries = 1;
        this.maxRetries = isTryAgain.retries() == -1 ? this.retries : isTryAgain.retries();
        ConcurrencyFailureException lockFailureException;
        String lockType;
        do {
            try {
                if (retries > 1) {
                    cleanArgs(point);
                }
                return point.proceed();
            } catch (PessimisticLockingFailureException e) { //悲观锁
                lockFailureException = e;
                lockType = "[悲观锁]";
            } catch (OptimisticLockingFailureException e) { //乐观锁
                lockFailureException = e;
                lockType = "[乐观锁]";
            } catch (Throwable e) {
                throw e;
            }
        } while (retries++ < this.maxRetries);
        if (logger.isDebugEnabled()) {
            logger.error(lockFailureException);
        }
        throw new RuntimeException("单据已变更，请刷新后重新操作！" );
    }

    /**
     * 优化乐观锁重试Mapping报错问题
     **/
    private void cleanArgs(ProceedingJoinPoint point) {
        Object[] args = point.getArgs();
        for (Object arg : args) {
            if (arg instanceof Request) {
                ((Request) arg).setData(null);
            }
        }
    }

}
